import { debounce } from 'radash'

/**
 * 缩放模式
 *
 * full: 视口比例与设计稿比例不一致时，强制拉伸. 特点: 不会留白，但内容可能会因为缩放导致变形
 * minScale: 视口比例与设计稿比例不一致时，使用宽高缩放比较小的一个. 特点: 内容不会因为缩放导致变形，但会留白
 * maxScale: 视口比例与设计稿比例不一致时，使用宽高缩放比较大的一个. 特点: 内容不会因为缩放导致变形, 但会有滚动条
 */
type ScaleMode = 'full' | 'minScale' | 'maxScale'
const autoScaleStyleDomId = 'auto-scale-style'
export interface ScaleViewOption {
  /**
   * 将哪个html的dom元素设置为缩放元素. 默认:body
   */
  el?: string | HTMLElement
  /**
   * body内容溢出时, 是否隐藏滚动条. 默认: true
   */
  bodyOverflowHidden?: boolean
  /**
   * el的定位方式. 默认: absolte
   */
  position?: 'absolute' | 'fixed'
  /**
   * 设计稿尺寸
   * 默认: width: 1920 height: 1080
   */
  designSize?: {
    /**
     * 设计稿宽度
     */
    width: number
    /**
     * 设计稿高度
     */
    height: number
  }
  /**
   * 缩放模式. 默认: full
   */
  scaleMode?: ScaleMode
}
export class ScaleView {
  private el: HTMLElement = document.body
  private bodyOverflowHidden = true
  private designSize = { width: 1920, height: 1080 }
  private scaleMode: ScaleMode = 'full'
  private position: 'absolute' | 'fixed' = 'absolute'

  constructor(option: ScaleViewOption = {}) {
    const {
      el,
      bodyOverflowHidden = true,
      designSize = { width: 1920, height: 1080 },
      scaleMode = 'full',
      position = 'absolute',
    } = option
    this.bodyOverflowHidden = bodyOverflowHidden
    if (typeof el === 'string') {
      const dom = document.querySelector<HTMLElement>(el)
      if (!dom) {
        throw `通过 【${el}】 未匹配到dom元素`
      }
      this.el = dom
    } else if (el) {
      this.el = el
    }
    this.position = position
    this.designSize = designSize
    this.scaleMode = scaleMode
    this.init()
  }
  destory() {
    window.removeEventListener('resize', this.updateScale)
    this.el.style.width = ''
    this.el.style.height = ''
    this.el.style.left = ''
    this.el.style.top = ''
    this.el.style.position = ''
    this.el.style.transform = ''
    this.el.style.transformOrigin = ''
  }
  private getOrCreateStyleDom(id: string) {
    let styleDom = document.getElementById(id)
    if (!styleDom) {
      styleDom = document.createElement('style')
      styleDom.lang = 'text/css'
      styleDom.id = id
      document.body.appendChild(styleDom)
    }
    return styleDom
  }
  private init() {
    // 必须设置body的overflow = 'hidden'，否则，body会因为临时的窗口大小变化而出现滚动条（最终实际不应该出现，但中间过程出现了），导致后续document.documentElement.clientWidth，document.documentElement.clientHeight不包含滚动条的大小
    if (this.bodyOverflowHidden) {
      document.body.style.overflow = 'hidden'
    }
    const dynScaleDomWrapperDom = document.createElement('div')
    dynScaleDomWrapperDom.id = 'dynRevertScaleDomWrapperDom'
    dynScaleDomWrapperDom.classList.add('revertAutoScaleView')
    this.el.appendChild(dynScaleDomWrapperDom)

    this.updateScale()
    const updateScaleDebounce = debounce({ delay: 300 }, this.updateScale)
    window.addEventListener('resize', updateScaleDebounce.bind(this))

    // 将元素的大小定义为设计稿的大小
    this.el.style.height = `${this.designSize.height}px`
    this.el.style.width = `${this.designSize.width}px`
    // 将transfrom变化的源点设置为左上顶点(默认为:中心点)
    this.el.style.transformOrigin = '0 0'
    this.el.style.position = this.position
    // this.el.style.overflow = 'hidden'
  }
  private updateScale() {
    const autoScaleStyleDom = this.getOrCreateStyleDom(autoScaleStyleDomId)
    const { widthScale, heightScale, minScale, maxScale } = this.computeScale()
    let transformScale = ''
    let revertTransformScale = ''
    if (this.scaleMode === 'full') {
      // 为了使内容充满视口, 需要分别对宽高进行缩放
      transformScale = `scale(${widthScale},${heightScale})`
      this.el.style.transform = transformScale
      revertTransformScale = `scale(${1 / widthScale},${1 / heightScale})`
      // 因为是拉伸铺满模式，所以将元素的起始位置设置为视口的左上顶点
      this.el.style.left = '0'
      this.el.style.top = '0'
    } else if (this.scaleMode === 'minScale') {
      // 因为是等比缩放模式，所以将元素的位置设置为上下都居中的位置
      this.el.style.left = '50%'
      this.el.style.top = '50%'
      // 等比缩放则只要使用宽度缩放比和高度缩放比中较小的那个设置为宽高的公共缩放比即可，translate3d(-50%, -50%, 0) 用于实现元素居中
      transformScale = `scale(${minScale})`
      this.el.style.transform = `${transformScale} translate3d(-50%, -50%, 0)`
      revertTransformScale = `scale(${1 / minScale})`
    } else if (this.scaleMode === 'maxScale') {
      transformScale = `scale(${maxScale})`
      this.el.style.transform = `${transformScale}`
      revertTransformScale = `scale(${1 / maxScale})`
    }
    autoScaleStyleDom.innerHTML = `.autoScaleView{transform:${transformScale};translateZ(0);will-change: transform;backface-visibility: hidden;} .revertAutoScaleView{transform:${revertTransformScale};translateZ(0);will-change: transform;backface-visibility: hidden;}`
  }
  computeScale() {
    const { width: viewPortWidth, height: viewPortHeight } =
      this.getViewPortSize()
    // 计算宽度缩放比
    const widthScale = this.computedScale(viewPortWidth, this.designSize.width)
    // 计算高度缩放比
    const heightScale = this.computedScale(
      viewPortHeight,
      this.designSize.height
    )
    // 如果是等比缩放，则需要使用最小的缩放比，避免内容被拉伸
    const minScale = Math.min(widthScale, heightScale)
    const maxScale = Math.max(widthScale, heightScale)
    return { widthScale, heightScale, minScale, maxScale }
  }
  private computedScale(realSize: number, designSize: number) {
    return realSize / designSize
  }
  private getViewPortSize() {
    // 获取浏览器视口宽/高
    const width = document.documentElement.clientWidth
    const height = document.documentElement.clientHeight
    console.log('视口大小', width, height)
    return { width, height }
  }
}
